function varargout = TLManageDlg(varargin)

% TL_MANAGE_EVENTS_DLG provides a GUI for managing events
%
% INPUT ARGUMENTS:
%   action     denotes an action string for dialog handling
%   userData   data structure holding dialog data
%
% OUTPUT ARGUMENTS:
%   userData   see INPUT ARGUMENTS
%
% AUTHOR:
%   T. Pietzsch
%
% Copyright (C) 2002 dSPACE GmbH, GERMANY

% $Workfile: TLManageDlg.m $ $Revision: 1.2 $ $Date: 2004/04/28 12:40:18MESZ $
% $Archive: /sw/TargetLink/Distribution/TargetLink_Base/Matlab/Tl/dlg/TLManageDlg.m $

% default input value
if ~nargin,
   varargin{1} = 'Create';
   varargin{2} = '';
end

% get action string
action = varargin{1};
if ~ischar(action),
   ds_error_msg('Invalid action string.');
   return;
end

% perform selected action
switch(lower(action)),
   case 'getdatadescription'
      % return the block data description
      varargout{1} = i_GetDataDescription;
      if ds_error_check, return; end
   case 'create'
      % create the dialog window
      ds_error_none;
      varargout{1} = i_Create(varargin{2});
      if ds_error_check, return; end

   %---------------------------------------
   % standard callbacks
   %---------------------------------------

   case 'sync'
      % synchronize data after user input
      varargout{1} = i_Sync(varargin{2});
      if ds_error_check, return; end
   case 'apply'
      % callback for Apply button
      varargout{1} = i_Apply(varargin{2});
      if ds_error_check, return; end  
   case 'help'
      ds_error_none;
      varargout{1} = i_Help(varargin{2});
      if ds_error_check, return; end
   case 'cancel'
      % callback for Cancel button
      varargout{1} = i_Cancel(varargin{2});
      if ds_error_check, return; end 
   case 'close'
      % callback for Ok button
      varargout{1} = i_Close(varargin{2});
      if ds_error_check, return; end
      
   %---------------------------------------
   % dialog specific callbacks
   %---------------------------------------
   
   case 'addbutton'
      % add a new event to list 
      varargout{1} = i_AddEvent(varargin{2});
      if ds_error_check, return; end

   case 'deletebutton'
      % delete the selected event
      varargout{1} = i_DeleteEvent(varargin{2});
      if ds_error_check, return; end
      
   otherwise
      ds_error_msg(['Invalid action string"',action,'"']);
      return;
end

%-------------------------------------------------------------------------------
% private function returning block data description
%-------------------------------------------------------------------------------

function dataDescription = i_GetDataDescription

% callback actions
dataDescription.callBackActions = { ...
   'getdatadescription', ...
   'sync', ...
   'apply', ...
   'addbutton', ...
   'deletebutton' ...
};

% property names
dataDescription.propertyNames = { ...
   'revisionNumber', ...
   'eventList', ...
   'mask', ...
   'description', ...
};

% revision number
dataDescription.revisionNumber.dataType     = 'integer';
dataDescription.revisionNumber.validValues  = [];
dataDescription.revisionNumber.defaultValue = 1;
dataDescription.revisionNumber.attributes   = {'read-only','hidden'};

% task
dataDescription.task.dataType               = 'string';
dataDescription.task.validValues            = {};
dataDescription.task.defaultValue           = '';
dataDescription.task.attributes             = {'hidden','never-save'};

% event list
dataDescription.eventList.dataType          = 'string array';
dataDescription.eventList.validValues       = {};
dataDescription.eventList.defaultValue      = [];
dataDescription.eventList.attributes        = {};

% event name
dataDescription.eventName.dataType          = 'string';
dataDescription.eventName.validValues       = {};
dataDescription.eventName.defaultValue      = 'none';
dataDescription.eventName.attributes        = {};

% mask
dataDescription.mask.dataType               = 'string';
dataDescription.mask.validValues            = {};
dataDescription.mask.defaultValue           = '';
dataDescription.mask.attributes             = {};

% description
dataDescription.description.dataType        = 'string';
dataDescription.description.validValues     = {};
dataDescription.description.defaultValue    = '';
dataDescription.description.attributes      = {};


%-------------------------------------------------------------------------------
% private function creating the dialog
%-------------------------------------------------------------------------------

function userData = i_Create(task)

% default return value
userData = [];

% get the data description for all uicontrols
dataDescription = i_GetDataDescription;

% check if task object exist
ddPath = ['/Pool/RTOS/Tasks/',task];
[bExist,hTask] = dsdd('exist',ddPath);
if ~bExist,
   ds_error_msg(sprintf([ ...
      'Task does not exist in current Data Dictionary\n', ...
      'Please load the appropriate Data Dictionary from file,\n', ...
      'or specify the correct task.']), ...
      'Title',             'Missing Task', ...
      'MessageNumber',     0);
   return;
end
dataDescription.task.defaultValue = task;

% constants used for position calculation
colDelta = 5;

% get control size for text fields
[labelWidth,labelHeight] = tl_get_uicontrol_size( ...
   'Strings',        {'Event name:'}, ...
   'Style',          'text');
labelSize = [labelWidth,labelHeight];

% get control size for button group
[buttonWidth,buttonHeight] = tl_get_uicontrol_size( ...
   'Strings',        {'<-- Deselect'}, ...
   'Style',          'pushbutton');

% adapt width to align controls
listboxWidth = 200;
listboxHeight = 175;
descEditWidth = 2 * listboxWidth ...
              + buttonWidth ...
              + colDelta ...
              - labelSize;

%---------------------------------------
% description
%---------------------------------------

descLabel = { ...
   'Tag',               'descLabel', ...
   'Style',             'text', ...
   'UiControlSize',     labelSize, ...
   'MessageText',       'Description:' ...
   };
   
descEditField = { ...
   'Tag',               'description', ...
   'Style',             'edit', ...
   'SetPosition',       'll = GetPoint(''descLabel'',''lr'') + colDelta;', ...
   'UiControlWidth',    descEditWidth ...
   };

%---------------------------------------
% event name and mask
%---------------------------------------

maskLabel = { ...
   'Tag',               'maskLabel', ...
   'Style',             'text', ...
   'SetPosition',       'll = GetPoint(''descLabel'',''ul'') + rowDelta + 4 * pxY;', ...
   'UiControlSize',     labelSize, ...
   'MessageText',       'Mask (hex):' ...
   };
maskEditField = { ...
   'Tag',               'mask', ...
   'Style',             'edit', ...
   'SetPosition',       'll = GetPoint(''description'',''ul'') + rowDelta;', ...
   'UiControlWidth',    listboxWidth - labelSize - colDelta ...
   };
eventNameLabel = { ...
   'Tag',               'eventNameLabel', ...
   'Style',             'text', ...
   'SetPosition',       'll = GetPoint(''maskLabel'',''ul'') + rowDelta + 4 * pxY;', ...
   'UiControlSize',     labelSize, ...
   'MessageText',       'Event name:' ...
   };
eventNameEdit = { ...
   'Tag',               'eventName', ...
   'Style',             'edit', ...
   'SetPosition',       'll = GetPoint(''mask'',''ul'') + rowDelta;', ...
   'UiControlWidth',    listboxWidth - labelSize - colDelta ...
   };

%---------------------------------------
% buttons to add, edit and delete events
%---------------------------------------

addButton = { ...
   'Tag',               'addButton', ...
   'Style',             'pushbutton', ...
   'SetPosition',       'll = GetPoint(''eventNameLabel'',''ul'') + rowDelta + 4 * pxY;', ...
   'UiControlWidth',     buttonWidth, ...
   'LabelStrings',      'Add' ...
   };

deleteButton = { ...
   'Tag',               'deleteButton', ...
   'Style',             'pushButton', ...
   'SetPosition',       'll = GetPoint(''addButton'',''lr'') + colDelta;', ...
   'UiControlWidth',     buttonWidth, ...
   'LabelStrings',      'Delete' ...
   };

%---------------------------------------
% event selection listbox pair
%---------------------------------------

eventSelection = { ...
   'Tag',               'eventList', ...
   'Style',             'IncludeExcludeTableListbox', ...
   'Headline',          {'Defined events','Selected Events'}, ...
   'LabelStrings',      {'none'}, ...
   'SetPosition',       'll = GetPoint(''addButton'',''ul'') + rowDelta;', ...
   'UiControlSize',     [listboxWidth,listboxHeight], ...
   'AssociatedButton',  {'<-- Deselect','Select -->'} ...
   };

userData = ds_generic_dlg('Create', ...
   'FigureTag',         '#Fig_TLManageDlg', ...
   'Title',             'Event management', ...
   ... 'WindowStyle',       'modal', ...
   'DataDescription',   dataDescription, ...
   'CallbackFcn',       mfilename, ...
   eventSelection{:}, ...
   deleteButton{:}, ...
   addButton{:}, ...
   eventNameLabel{:}, ...
   eventNameEdit{:}, ...
   maskEditField{:}, ...
   maskLabel{:}, ...
   descEditField{:}, ...
   descLabel{:}, ...
   'ButtonName',        'OK', ...
   'ButtonName',        'Cancel', ...
   'ButtonName',        'Apply', ...
   'DefaultButton',     'Cancel' ...
   );

%-------------------------------------------------------------------------------
% private function to synchronize data after user input
%-------------------------------------------------------------------------------

function userData = i_Sync(userData);

currentUiControl = userData.currentUiControl;
defaultColor = get(userData.hDlg,'Color');

% initialize dialog data
if currentUiControl == 0,
   userData = i_Init(userData);
   if ds_error_check,
      % close dialog figure because it's incapable of being used
      % userData.bClose = 1;
      return
   end
   currentUiControl = userData.dataDescription.eventList.uiControl;
end

uiEventList = userData.dataDescription.eventList.uiControl;
uiEventName = userData.dataDescription.eventName.uiControl;
uiEventMask = userData.dataDescription.mask.uiControl;
uiEventDesc = userData.dataDescription.description.uiControl;

% switch between different synchronizing procedures
switch userData.uiControl(currentUiControl).tag
   case 'eventList'
      % transfer name and mask into edit fields
      currentEvent = userData.uiControl(uiEventList).currElement;
      if length(currentEvent) > 1 | isempty(userData.uiControl(uiEventList).value(currentEvent).display),
         % disable edit controls
         userData.uiControl(uiEventName).value = '';
         userData.uiControl(uiEventName).bEnabled = 0;
         userData.uiControl(uiEventMask).value = '';
         userData.uiControl(uiEventMask).bEnabled = 0;
         userData.uiControl(uiEventDesc).value = '';
         userData.uiControl(uiEventDesc).bEnabled = 0;
      else
         userData.uiControl(uiEventName).value = ...
            userData.uiControl(uiEventList).value(currentEvent).display.name;
         userData.uiControl(uiEventName).bEnabled = 1;
         userData.uiControl(uiEventMask).value = ...
            userData.uiControl(uiEventList).value(currentEvent).mask;
         userData.uiControl(uiEventMask).bEnabled = 1;
         userData.uiControl(uiEventDesc).value = ...
            userData.uiControl(uiEventList).value(currentEvent).description;
         userData.uiControl(uiEventDesc).bEnabled = 1;
      end
   case 'eventName'
      userData = i_Rename(userData);

   case 'mask'
      userData = i_EditMask(userData);

   case 'description'
      userData = i_EditDescription(userData);
      
end

userData.bApplyUiControls = 1;
userData.bApplyData       = 0;
userData.bClose           = 0;

%-------------------------------------------------------------------------------
% private function providing OK Button callback
%-------------------------------------------------------------------------------

function userData = i_Ok(userData);

userData = i_Apply(userData);

%-------------------------------------------------------------------------------
% private function providing cancel button callback
%-------------------------------------------------------------------------------

function userData = i_Cancel(userData);

%-------------------------------------------------------------------------------
% private function initializeing dialog fields
%-------------------------------------------------------------------------------

function userData = i_Init(userData);

% supplement userData structure with lists holding
% added and deleted events
userData.addedEvent   = {};
userData.deletedEvent = {};
userData.renamedEvent = {};
userData.editedEvent  = {};

% get selected events from task object
userData.task = userData.dataDescription.task.defaultValue;
ddPath   = ['/Pool/RTOS/Tasks/',userData.task];
hTaskEvent   = dsdd('GetEvents',ddPath);
eventRef = dsdd('GetPropertyNames',hTaskEvent);
selectedEvent = {};
for idx = 1:length(eventRef),
   % [hSelectedEvent,errCode] = dsdd('GetEventsEventRefTarget',ddPath,eventRef{idx});
   [selectedEvent{idx},errCode] = dsdd('Get',hTaskEvent,eventRef{idx});
   if dsdd_check_msg(errCode),
      return
   end
end

% index for event list uicontrol
uiEventList = userData.dataDescription.eventList.uiControl;

% events are stored in RTOS branch of Data Dictionary pool
ddPath = '/Pool/RTOS/Events';
[hEvent,errCode] = dsdd('GetChildren',ddPath);
if dsdd_check_msg(errCode),
   return
end
if isempty(hEvent),
   eventNameList = {'none'};
   userData.uiControl(uiEventList).value.display     = [];
   userData.uiControl(uiEventList).value.mask        = '';
   userData.uiControl(uiEventList).value.description = '';
else   
   for idx = 1:length(hEvent),
      eventName     = dsdd('GetAttribute',hEvent(idx),'Name');
      eventMask     = dsdd('GetMask',hEvent(idx));
      eventDesc     = dsdd('GetDescription',hEvent(idx));
      bSelected = any(strcmp(eventName,selectedEvent));
      eventNameList{idx} = eventName;
      userData.uiControl(uiEventList).value(idx).display.name = eventName;
      userData.uiControl(uiEventList).value(idx).mask         = eventMask;
      userData.uiControl(uiEventList).value(idx).description  = eventDesc;
      userData.uiControl(uiEventList).value(idx).bSelected    = bSelected;
      userData.uiControl(uiEventList).value(idx).origName     = eventName;
   end
end

% copy value to prefValue
userData.uiControl(uiEventList).prevValue = ...
   userData.uiControl(uiEventList).value;

% write event list to uiControl.dataStrings
userData.uiControl(uiEventList).dataStrings = eventNameList;

%-------------------------------------------------------------------------------
% private function to add events 
%-------------------------------------------------------------------------------

function userData = i_AddEvent(userData)

% find list entries beginning with 'NewEvent'
uiEventList = userData.dataDescription.eventList.uiControl;
eventNameList = userData.uiControl(uiEventList).dataStrings;
idxDefaultNameEvent = find(strncmp(eventNameList,'NewEvent',8));

% find a unique name
if isempty(idxDefaultNameEvent),
   newEventName = 'NewEvent';
else
   defaultNameEvent = eventNameList(idxDefaultNameEvent);
   woNumIdx = find(strcmp(defaultNameEvent,'NewEvent'));
   if isempty(woNumIdx),
      % string without number extension can be used
      newEventName = 'NewEvent';
   else
      % remove string without number extension from list
      defaultNameEvent(woNumIdx) = [];
      usedNumber = [];
      % get all numbers in use
      for m = 1:length(defaultNameEvent),
         num = str2num(strrep(defaultNameEvent{m},'NewEvent',''));
         usedNumber = [usedNumber,num];
      end
      % get smallest unused number
      if isempty(usedNumber),
         newEventName = 'NewEvent1';
      else
         unusedNumber = setxor([1:(max(usedNumber) + 1)],usedNumber);
         newEventName = ['NewEvent',sprintf('%d',unusedNumber(1))];
      end
   end
end

% remove 'none' from list
newEventIdx = find(strcmp(eventNameList,'none'));
if isempty(newEventIdx),
   % new event is inserted at end of list
   newEventIdx = length(userData.uiControl(uiEventList).value) + 1;
end

% supplement event list
eventNameList{newEventIdx} = newEventName;
userData.uiControl(uiEventList).dataStrings = eventNameList;
userData.uiControl(uiEventList).currElement = newEventIdx;
userData.uiControl(uiEventList).value(newEventIdx).bSelected = 0;
userData.uiControl(uiEventList).value(newEventIdx).display.name = newEventName;
userData.uiControl(uiEventList).value(newEventIdx).mask         = 'AUTO';
userData.uiControl(uiEventList).value(newEventIdx).description  = '';
userData.uiControl(uiEventList).value(newEventIdx).origName     = '';
userData.addedEvent = [userData.addedEvent,{newEventName}];

% deselecting list entries will result in selecting last
% entry while applying uiControlData
set(userData.uiControl(uiEventList).handle, ...
   'Value',          []);

% synchronize data with common sync function
userData.currentUiControl = uiEventList;
userData = i_Sync(userData);

%-------------------------------------------------------------------------------
% private function to delete events
%-------------------------------------------------------------------------------

function userData = i_DeleteEvent(userData)

% get relevant indices 
uiEventList = userData.dataDescription.eventList.uiControl;
delEventIdx = userData.uiControl(uiEventList).currElement;

% add event to list of deleted events
event = userData.uiControl(uiEventList).dataStrings(delEventIdx);
userData.deletedEvent = [userData.deletedEvent,event];
userData.deletedEvent = unique(userData.deletedEvent);

% remove event from list of added events
idx = find(strcmp(event,userData.addedEvent));
if ~isempty(idx),
   userData.addedEvent(idx) = [];
end

% update uiControl data
userData.uiControl(uiEventList).dataStrings(delEventIdx) = [];
userData.uiControl(uiEventList).value(delEventIdx) = [];

% update current element
numElements = length(userData.uiControl(uiEventList).dataStrings);
if numElements,
   currentElement = min(min(delEventIdx),numElements);
   set(userData.uiControl(uiEventList).handle, ...
      'Value',          currentElement);
   userData.uiControl(uiEventList).currElement = currentElement;
else
   % no elements
   set(userData.uiControl(uiEventList).handle, ...
      'Value',          []);
   userData.uiControl(uiEventList).dataStrings = {'none'};
   userData.uiControl(uiEventList).value(1).display      = [];
   userData.uiControl(uiEventList).value(1).mask         = '';
   userData.uiControl(uiEventList).value(1).description  = '';
   userData.uiControl(uiEventList).currElement = 1;
end

% synchronize data with common sync function
userData.currentUiControl = uiEventList;
userData = i_Sync(userData);

%-------------------------------------------------------------------------------
% private function to rename an event
%-------------------------------------------------------------------------------

function userData = i_Rename(userData);

uiEventList  = userData.dataDescription.eventList.uiControl;
currentEvent = userData.uiControl(uiEventList).currElement;

% preserve old name temporarily
oldName = userData.uiControl(uiEventList).dataStrings{currentEvent};
newName = userData.uiControl(userData.currentUiControl).value;
if strcmp(oldName,newName),
   return
end

% check if name is already in use
if any(strcmp(newName,userData.uiControl(uiEventList).dataStrings)),
   set(userData.uiControl(userData.currentUiControl).handle, ...
      'ForegroundColor',      'r');
   ds_error_msg(sprintf( ...
      ['An event named ',newName,' already exists.\n', ...
       'Please modify the name for the event!']));
   return
end

% change name in several lists
userData.uiControl(uiEventList).dataStrings{currentEvent} = ...
   newName;
userData.uiControl(uiEventList).value(currentEvent).display.name = ...
   newName;

% update lists with added, renamed and deleted events
origName = userData.uiControl(uiEventList).value(currentEvent).origName;
if ~isempty(origName),
   userData.renamedEvent = unique([userData.renamedEvent,{origName}]);
end
idx = find(strcmp(userData.addedEvent,oldName));
if ~isempty(idx),
   bNew = 1;
   userData.addedEvent{idx} = newName;
else
   bNew = 0;
end
idx = find(strcmp(userData.deletedEvent,oldName));
if ~isempty(idx),
   userData.deletedEvent{idx} = newName;
end
idx = find(strcmp(userData.editedEvent,oldName));
if ~isempty(idx),
   userData.editedEvent{idx} = newName;
end

%-------------------------------------------------------------------------------
% private function to change mask string
%-------------------------------------------------------------------------------

function userData = i_EditMask(userData);

% transfer mask string into list of events
eventMask = userData.uiControl(userData.currentUiControl).value;
uiEventList  = userData.dataDescription.eventList.uiControl;
currentEvent = userData.uiControl(uiEventList).currElement;
userData.uiControl(uiEventList).value(currentEvent).mask = ...
   eventMask;

% supplement list of edited events
event = userData.uiControl(uiEventList).dataStrings(currentEvent);
if isempty(userData.addedEvent) | ~strcmp(event,userData.addedEvent),
   userData.editedEvent = [userData.editedEvent, event];
end

%-------------------------------------------------------------------------------
% private function to change description string
%-------------------------------------------------------------------------------

function userData = i_EditDescription(userData);

% transfer description string into list of events
eventDescription = userData.uiControl(userData.currentUiControl).value;
uiEventList  = userData.dataDescription.eventList.uiControl;
currentEvent = userData.uiControl(uiEventList).currElement;
userData.uiControl(uiEventList).value(currentEvent).description = ...
   eventDescription;

% supplement list of edited events
event = userData.uiControl(uiEventList).dataStrings(currentEvent);
if isempty(userData.addedEvent) | ~strcmp(event,userData.addedEvent),
   userData.editedEvent = [userData.editedEvent, event];
end

%-------------------------------------------------------------------------------
% private function to apply data
%-------------------------------------------------------------------------------

function userData = i_Apply(userData);

ddPath = '/Pool/RTOS/Events';
[bExist,hEventNode] = dsdd('Exist',ddPath);
if ~bExist,
   ds_error_msg(['Assertion failure in ',mfilename]);
end

% first delete events
for m = 1:length(userData.deletedEvent),
   [bExists,hEvent] = dsdd('Exist',userData.deletedEvent{m}, ...
      'Parent',hEventNode);
   if bExist,
      dsdd('Delete',hEvent);
   end
end

% apply new names for renamed events
uiEventList = userData.dataDescription.eventList.uiControl;
for m = 1:length(userData.renamedEvent),
   [bExist,hEvent] = dsdd('exist',userData.renamedEvent{m}, ...
      'Parent',hEventNode);
   if ~bExist,
      disp('Cannot apply renaming of event ',userData.renamedEvent{m});
      continue
   end
   idx = find(strcmp(userData.renamedEvent{m}, ...
      {userData.uiControl(uiEventList).value.origName}));
   newName = userData.uiControl(uiEventList).value(idx).display.name;
   errCode = dsdd('SetAttribute',hEvent,'Name',newName);
   if dsdd_check_msg(errCode),
      userData.uiControl(uiEventList).dataStrings(idx) = ...
         userData.renamedEvent{m};
      userData.uiControl(uiEventList).value(idx).display.name = ...
         userData.renamedEvent{m};
   else
      userData.uiControl(uiEventList).value(idx).origName = ...
         userData.renamedEvent{m};
   end
end

% add newly inserted events
for m = 1:length(userData.addedEvent),
   [hEvent,errCode] = dsdd('Create',userData.addedEvent{m}, ...
      'Parent',            hEventNode, ...
      'ObjectKind',        'Event');
   if dsdd_check_msg(errCode)
      % don't return if creation failed
      continue
   end
   % set mask
   mask = userData.uiControl(uiEventList).value(m).mask;
   if ~isempty(mask)
      errCode = dsdd('SetMask',hEvent,mask);
      dsdd_check_msg(errCode);
   end
   % set description
   description = userData.uiControl(uiEventList).value(m).description;
   if ~isempty(description)
      errCode = dsdd('SetDescription',hEvent,description);
      dsdd_check_msg(errCode);
   end
end

% apply changes of mask and description values 
editedEvent = unique(userData.editedEvent);
for m = 1:length(editedEvent),
   idx = find(strcmp( ...
      editedEvent{m},userData.uiControl(uiEventList).dataStrings));
   mask        = userData.uiControl(uiEventList).value(idx).mask;
   description = userData.uiControl(uiEventList).value(idx).description;
   [bExist,hEditedEvent] = dsdd('exist',editedEvent{m}, ...
      'Parent',hEventNode);
   if bExist,
      errCode = dsdd('SetMask',hEditedEvent,mask);
      dsdd_check_msg(errCode);
      errCode = dsdd('SetDescription',hEditedEvent,description);
      dsdd_check_msg(errCode);
   else 
      % don't return if creation failed
      dsdd_check_msg(5040)
   end
end

% write selected events to task
oldEventList = {};
[bExist,hTask] = dsdd('Exist',userData.task, ...
   'Parent','/Pool/RTOS/Tasks');
if bExist,
   hEventNode = dsdd('GetEvents',hTask);
   refPropertyList = dsdd('GetPropertyNames',hEventNode);
   for m = 1:length(refPropertyList),
      oldEventList{m} = dsdd('Get',hEventNode,refPropertyList{m});
   end
   newEventIdx = find([userData.uiControl(uiEventList).value.bSelected]);
   newEventList = userData.uiControl(uiEventList).dataStrings(newEventIdx);
   [changedEvents,deleteIdx,createIdx] = setxor(oldEventList,newEventList);
   for m = deleteIdx,
      dsdd('delete',hEventNode,refPropertyList{m});
   end
   for m = createIdx,
      propertyName = [newEventList{m},'Ref'];
      dsdd('AddEventRef',hTask,propertyName,newEventList{m});
   end
end

% clear lists
userData.deletedEvent = {};
userData.renamedEvent = {};
userData.addedEvent   = {};
userData.editedEvent  = {};
